/*
 * Copyright (C) 2018 Logos Technology Ltd.
 */

package com.logos.common.base;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.Application;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;

import androidx.multidex.MultiDex;

import com.logos.common.MainActivity;
import com.logos.tool.preference.PreferenceKeys;
import com.logos.tool.preference.Preferences;
import com.logos.tool.log.LogUtil;
import com.squareup.leakcanary.LeakCanary;
import com.zxy.recovery.core.Recovery;

import java.util.List;
import java.util.Stack;

import io.objectbox.BoxStore;

/**
 * @author Young Ken
 */
public class BaseApplication extends Application {

    private static final String TAG = "common.BaseApplication";
    public static final String STATISTICS_PAGE_NAME = "page_name";
    public static final String STATISTICS_PAGE_START_TIME = "page_start_time";
    public static final String STATISTICS_PAGE_END_TIME = "page_end_time";
    public static final String STATISTICS_PAGE_DURATION = "page_duration";
    public static final String STATISTICS_TRACK_TYPE_LIVE = "page_live";
    private static final String[] MODULESLIST = {"com.logos.tool.base.ToolApplication",
            "com.logos.ui.base.UiApplication"};

    private static BaseApplication sAppContext;
    private Thread.UncaughtExceptionHandler mDefaultUncaughtExceptionHandler;

    private boolean mTest = false;
    /*
     * Record class all activities of current application, including those which are temporarily
     * killed by system (in low memory case).
     * The list doesn't guarantee order of activities in activity-stack.
     */
    private List<Class> mActivityList;
    private int mForegroundActivityCount;
    private static BoxStore sBoxStore;

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        sAppContext = this;
        MultiDex.install(this);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        prepareActivityStackManagement();
        Log.i(TAG, "BaseApplication JPushInterface init()");
        // Start an activity to show uncaught exception properly
        mDefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(final Thread thread, final Throwable ex) {
                LogUtil.error(TAG, "!! Application Crashes !! " + ex, ex);

//                String promptMsg;
//                if (ex instanceof SQLiteException) {
//                    promptMsg = getString(R.string.prompt_restart_phone_on_data_error);
//                } else {
//                    promptMsg = getString(R.string.prompt_restart_phone_on_unknown_error);
//                }
//
//                Intent crashedIntent = new Intent(this, UncaughtExceptionShower.class);
//                crashedIntent.putExtra("KEY", promptMsg);
//                crashedIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//                startActivity(crashedIntent);
                Preferences.build(getAppContext()).putString(PreferenceKeys.BUG_REPORT_TASK_ID,
                        String.valueOf(System.currentTimeMillis()));
                mDefaultUncaughtExceptionHandler.uncaughtException(thread, ex);
            }
        });
        Recovery.getInstance()
                .debug(true)
                .recoverInBackground(false)
                .recoverStack(true)
                .mainPage(MainActivity.class)
                .recoverEnabled(true)
                .silent(false, Recovery.SilentMode.RECOVER_ACTIVITY_STACK)
                .init(this);

        if (!LeakCanary.isInAnalyzerProcess(this)) {
            LeakCanary.install(this);
        }
    }

    public static BaseApplication getAppContext() {
        return sAppContext;
    }

    /**
     * Check if there is an activity of current application whose class equals to given class.
     * <p></p>
     * NOTE: even if the activity is temporarily killed by system (in low memory case), this
     * function still returns true. That is, return_init true doesn't mean the activity is currently
     * running.
     *
     * @param activityClz The class of an activity to check.
     * @return true if an activity of class activityClz is running; false otherwise.
     */
    public boolean isActivityExistent(Class activityClz) {
        if (null == activityClz) {
            return true;
        }

        for (Class clz : mActivityList) {
            if (activityClz.equals(clz)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check if there is an activity of current application whose class equals to given class.
     * <p></p>
     * NOTE: even if the activity is temporarily killed by system (in low memory case), this
     * function still returns true. That is, return_init true doesn't mean the activity is currently
     * running.
     *
     * @param activityClz The class of an activity to check.
     * @return true if an activity of class activityClz is running; false otherwise.
     */
    public boolean isActivityOnlyExistent(Class activityClz) {
        if (null == activityClz || mActivityList == null || mActivityList.size() != 1) {
            return false;
        }

        for (Class clz : mActivityList) {
            if (activityClz.equals(clz)) {
                return true;
            }
        }
        return false;
    }

    /**
     * the empty method.
     */
    public void logoutAndLaunchLoginPage() {
    }

    private void prepareActivityStackManagement() {
        if (null == mActivityList) {
            mActivityList = new Stack<>();
        } else {
            mActivityList.clear();
        }

        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                /*
                 * Because the activity is not removed from mActivityStack if it is shut down
                 * previously by system in low memory case, ignore it when the activity is
                 * being re-initialized.
                 */
                if (null == savedInstanceState) {
                    mActivityList.add(activity.getClass());
                }
            }

            @Override
            public void onActivityStarted(Activity activity) {
                // report app-brought-to-foreground event

            }

            @Override
            public void onActivityResumed(Activity activity) {
            }

            @Override
            public void onActivityPaused(Activity activity) {
            }

            @Override
            public void onActivityStopped(Activity activity) {
            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
            }

            @Override
            public void onActivityDestroyed(Activity activity) {

                if (activity.isFinishing()) {
                    for (int i = mActivityList.size() - 1; i >= 0; i--) {
                        Class clz = mActivityList.get(i);
                        if (clz.equals(activity.getClass())) {
                            mActivityList.remove(activity.getClass());
                            break;
                        }
                    }
                }
            }
        });
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        //TODO 发送全局广播
    }

    /**
     * is main thread
     *
     * @return is main return true
     */
    protected boolean isMainProcess() {
        int pid = android.os.Process.myPid();
        String processName = "";
        ActivityManager activityManager = (ActivityManager)
                getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningAppProcessInfo appProcess :
                activityManager.getRunningAppProcesses()) {
            if (appProcess.pid == pid) {
                processName = appProcess.processName;
                break;
            }
        }
        String packageName = getPackageName();
        return processName.equals(packageName);
    }

    /**
     * 关闭所有
     */
    public synchronized void finishAllActivity() {

        for (int i = mActivityList.size() - 1; i >= 0; i--) {
            Class clz = mActivityList.get(i);

            if (clz.getClass().isAssignableFrom(BaseActivity.class)) {
                BaseActivity activity = (BaseActivity) clz.cast(BaseActivity.class);
                if (activity != null) {
                    activity.finish();
                }
            }
        }
    }

    /**
     * Setting up the current running environment
     */
    public void initUnitTests() {
        //在测试的时候初始化
    }
}
